package com.site.common.quartz;

import com.site.pojo.ScheduleJob;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import java.util.Date;

/**
 * 定时任务管理
 */
public class QuartzJobManager {
    // 前缀
    private static final String JOB_NAME = "TASK_";

    //@Autowired
    private static SchedulerFactoryBean schedulerFactoryBean;
    //@Autowired
    //private Scheduler scheduler;

    public static void setSchedulerFactoryBean(SchedulerFactoryBean schedulerFactoryBean) {
        QuartzJobManager.schedulerFactoryBean = schedulerFactoryBean;
    }

    /**
     * 获取调度器
     * @return
     */
    public static Scheduler getScheduler(){
        return schedulerFactoryBean.getScheduler();
    }

    /**
     * 获取 jobkey
     * @param jobId
     * @return
     */
    public static JobKey getJobKey(Long jobId){
        return JobKey.jobKey(JOB_NAME + jobId);
    }

    /**
     * 获取 triggerKey
     * @param jobId
     * @return
     */
    public static TriggerKey getTriggerKey(Long jobId){
        return TriggerKey.triggerKey(JOB_NAME + jobId);
    }

    /**
     * 获取表达式触发器
     * @param scheduler
     * @param jobId
     * @return
     */
    public static CronTrigger getCronTrigger(Scheduler scheduler,Long jobId){
        try {
            return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));
        } catch (SchedulerException e) {
            throw new RuntimeException("获取定时任务CronTrigger出现异常", e);
        }
    }

    /**
     * 创建任务
     * @param scheduleJob 任务实体类（对应自定义的数据库任务表）
     * @return Date 返回创建任务成功后执行时间
     */
    public static Date createScheduleJob(ScheduleJob scheduleJob){
        try {
            JobKey jobKey = getJobKey(scheduleJob.getJobId());
            Scheduler scheduler = getScheduler();
            JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class).withIdentity(jobKey).build();
            // 不触发立即执行，等待下次Cron触发频率到达时刻开始按照Cron频率依次执行  withMisfireHandlingInstructionDoNothing
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()).withMisfireHandlingInstructionDoNothing();
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getJobId())).withSchedule(cronScheduleBuilder).build();
            jobDetail.getJobDataMap().put(ScheduleJob.JOB_PARAM_KEY,scheduleJob);
            if (scheduler.checkExists(jobKey)){
                scheduler.deleteJob(jobKey);
            }
            Date startTime = scheduler.scheduleJob(jobDetail,trigger);
            if (scheduleJob.getStatus() == 1){// 0表示继续，1表示暂停
                pauseJob(scheduleJob.getJobId());
            }
            return startTime;
        } catch (SchedulerException e) {
            throw new RuntimeException("创建定时任务失败",e);
        }
    }

    /**
     * 更新定时任务
     * @param scheduleJob 任务实体类（对应自定义的数据库任务表）
     * @return Date 返回更新任务成功后执行时间
     */
    public static Date updateScheduleJob(ScheduleJob scheduleJob){
        try {
            TriggerKey triggerKey = getTriggerKey(scheduleJob.getJobId());
            Scheduler scheduler = getScheduler();
            // 2.获取 cron 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()).withMisfireHandlingInstructionDoNothing();
            CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getJobId());
            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            trigger.getJobDataMap().put(ScheduleJob.JOB_PARAM_KEY,scheduleJob);
            Date updateTime = scheduler.rescheduleJob(triggerKey,trigger);
            if (scheduleJob.getStatus() == 1){// 0表示继续，1表示暂停
                pauseJob(scheduleJob.getJobId());
            }
            return updateTime;
        } catch (SchedulerException e) {
            throw new RuntimeException("更新定时任务失败",e);
        }
    }

    /**
     * 立即执行（让定时任务立即执行，**注意：暂停状态下的定时任务，如果立即执行，只会执行一次，相当于一次性执行）
     * @param scheduleJob 任务实体类（对应自定义的数据库任务表）
     */
    public static void run(ScheduleJob scheduleJob){
        try {
            // 参数
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(ScheduleJob.JOB_PARAM_KEY,scheduleJob);

            getScheduler().triggerJob(getJobKey(scheduleJob.getJobId()),dataMap);
        } catch (SchedulerException e) {
            throw new RuntimeException("立即执行定时任务失败",e);
        }
    }

    /**
     * 暂停任务
     * @param jobId 任务jobId
     */
    public static void pauseJob(Long jobId){
        try {
            getScheduler().pauseJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new RuntimeException("暂停定时任务失败",e);
        }
    }

    /**
     * 恢复任务
     * @param jobId 任务jobId
     */
    public static void resumeJob(Long jobId){
        try {
            getScheduler().resumeJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new RuntimeException("恢复定时任务失败",e);
        }
    }

    /**
     * 验证定时任务是否存在
     * @param jobId 任务id
     * @return
     */
    public static boolean check(Long jobId){
        try {
            return getScheduler().checkExists(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new RuntimeException("验证定时任务是否存在失败",e);
        }
    }

    /**
     * 删除定时任务
     * @param jobId 定时任务id
     */
    public static void deleteScheduleJob(Long jobId){
        Scheduler scheduler = getScheduler();
        try {
            scheduler.pauseTrigger(getTriggerKey(jobId));
            scheduler.unscheduleJob(getTriggerKey(jobId));
            scheduler.deleteJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new RuntimeException("删除定时任务失败",e);
        }
    }
}
